package com.cq.hd.member.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cq.hd.common.enums.biz.BusinessPointsMallProductTypeEnum;
import com.cq.hd.common.enums.biz.BusinessPointsMallStateEnum;
import com.cq.hd.common.enums.biz.PointsTypeEnum;
import com.cq.hd.common.enums.biz.UserPointsIncomeTypeEnum;
import com.cq.hd.common.pagination.Page;
import com.cq.hd.common.response.Throw;
import com.cq.hd.member.api.dto.BusinessChangePointsDto;
import com.cq.hd.member.api.dto.OrderItemPointsSettleDto;
import com.cq.hd.member.api.dto.ShareActivePointsDto;
import com.cq.hd.member.api.dto.UserPointsAccountPageDto;
import com.cq.hd.member.api.vo.BusinessPointsMallSettingsVo;
import com.cq.hd.member.api.vo.UserPointsAccountPageVo;
import com.cq.hd.member.mapper.*;
import com.cq.hd.member.po.*;
import com.cq.hd.member.service.TbUserPointsAccountService;
import com.cq.hd.order.api.TbOrderApi;
import com.cq.hd.order.api.dto.CreateOrderDto;
import com.cq.hd.order.api.vo.CreateOrderVo;
import com.cq.hd.product.api.TbActiveRedPacketBillApi;
import com.cq.hd.product.api.dto.PointsRedPacketDto;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.interceptor.DefaultTransactionAttribute;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Objects;

@Slf4j
@Service
public class TbUserPointsAccountServiceImpl extends ServiceImpl<TbUserPointsAccountMapper, TbUserPointsAccountPo> implements TbUserPointsAccountService {
    @Autowired
    private PlatformTransactionManager transactionManager;

    @Autowired
    private TbUserPointsDetailMapper tbUserPointsDetailMapper;

    @Autowired
    private TbBusinessMapper tbBusinessMapper;

    @Autowired
    private TbBusinessPointsMallSettingsMapper tbBusinessPointsMallSettingsMapper;

    @Autowired
    private TbBusinessPointsMallStatMapper tbBusinessPointsMallStatMapper;

    @Autowired
    private TbBusinessPointsMallProductMapper productMapper;

    @Autowired
    private TbActiveRedPacketBillApi redPacketBillApi;

    @Autowired
    private TbOrderApi orderApi;

    public TbUserPointsAccountPo getAccountWithCreate(Long businessId, Long userId) {
        TbUserPointsAccountPo accountPo = baseMapper.getAccount(businessId, userId);
        if (accountPo != null) {
            return accountPo;
        }

        TbUserPointsAccountPo po = new TbUserPointsAccountPo();
        po.setBusinessId(businessId);
        po.setUserId(userId);
        po.setRemainPoint(BigDecimal.ZERO);
        po.setTotalPoint(BigDecimal.ZERO);
        po.setCreateTime(LocalDateTime.now());
        po.setUpdateTime(LocalDateTime.now());
        boolean res = save(po);
        if (res) {
            tbBusinessPointsMallStatMapper.updateUserNum(businessId, 1);
        }

        return po;
    }

    /**
     * 订阅积分
     * @param businessId
     * @param userId
     * @return
     */
    public BigDecimal getSubscribePoints(Long businessId, Long userId) {
        TbBusinessPo businessPo = tbBusinessMapper.selectById(businessId);
        if (businessPo == null || businessPo.getDelState() != 0 || !businessPo.getPointsMallState().equals(BusinessPointsMallStateEnum.OPEN.getValue())) {
            return BigDecimal.ZERO;
        }

        TbUserPointsAccountPo accountPo = getAccountWithCreate(businessId, userId);
        if (accountPo == null || accountPo.getId() == null) {
            Throw.isBusinessException("积分账户创建失败");
        }

        BusinessPointsMallSettingsVo settingsVo = tbBusinessPointsMallSettingsMapper.getByBusinessId(businessId);
        if (settingsVo == null || settingsVo.getFirstSubscription().compareTo(BigDecimal.ZERO) <= 0) {
            return BigDecimal.ZERO;
        }

        ChangePointsData changePointsData = new ChangePointsData();
        changePointsData.points = settingsVo.getFirstSubscription();
        changePointsData.pointsType = PointsTypeEnum.SUBSCRIBE.getValue();
        changePointsData.title = "订阅策划人";
        changePointsAndAddLog(accountPo, changePointsData);

        return changePointsData.points;
    }

    /**
     * 取消订阅，退回订阅时获得的积分
     * @param businessId
     * @param userId
     * @return
     */
    public void returnSubscribePoints(Long businessId, Long userId) {
        TbUserPointsAccountPo accountPo = baseMapper.getAccount(businessId, userId);
        if (accountPo == null) {
            return;
        }

        // 积分没了，不用扣了
        if (accountPo.getRemainPoint().compareTo(BigDecimal.ZERO) <= 0) {
            return;
        }

        // 获取用户订阅获得的积分
        BigDecimal subscribePoints = tbUserPointsDetailMapper.getUserSubscribePoints(businessId, userId);
        if (subscribePoints == null || subscribePoints.compareTo(BigDecimal.ZERO) <= 0) {
            return;
        }

        // 不够扣的话，就扣减到0
        if (subscribePoints.compareTo(accountPo.getRemainPoint()) > 0) {
            subscribePoints = accountPo.getRemainPoint();
        }
        subscribePoints = subscribePoints.multiply(BigDecimal.valueOf(-1));

        ChangePointsData changePointsData = new ChangePointsData();
        changePointsData.points = subscribePoints;
        changePointsData.pointsType = PointsTypeEnum.SUBSCRIBE.getValue();
        changePointsData.title = "取消订阅策划人";
        changePointsAndAddLog(accountPo, changePointsData);
    }

    /**
     * 分享活动赚积分
     */
    @Override
    public void getShareActivePoints(ShareActivePointsDto shareActivePointsDto) {
        Long businessId = shareActivePointsDto.getBusinessId();
        Long userId = shareActivePointsDto.getUserId();

        TbBusinessPo businessPo = tbBusinessMapper.selectById(businessId);
        if (businessPo == null || businessPo.getDelState() != 0 || !businessPo.getPointsMallState().equals(BusinessPointsMallStateEnum.OPEN.getValue())) {
            return;
        }

        TbUserPointsAccountPo accountPo = getAccountWithCreate(businessId, userId);
        if (accountPo == null || accountPo.getId() == null) {
            Throw.isBusinessException("积分账户创建失败");
        }

        BusinessPointsMallSettingsVo settingsVo = tbBusinessPointsMallSettingsMapper.getByBusinessId(businessId);
        if (settingsVo == null) {
            return;
        }
        BigDecimal shareActivePoints = settingsVo.getShareActive();
        Integer shareDailyLimit = settingsVo.getShareDailyLimit();
        if (shareActivePoints == null || shareActivePoints.compareTo(BigDecimal.ZERO) <= 0) {
            return;
        }
        if (shareDailyLimit == null || shareDailyLimit <= 0) {
            return;
        }

        LocalDateTime now = LocalDateTime.now();
        Integer today = Integer.valueOf(now.format(DateTimeFormatter.ofPattern("yyyyMMdd")));
        // 今天赚了几次了
        Integer getCount = tbUserPointsDetailMapper.selectCount(new QueryWrapper<TbUserPointsDetailPo>()
                .eq("business_id", businessId)
                .eq("user_id", userId)
                .eq("points_type", PointsTypeEnum.SHARE.getValue())
                .eq("daily", today)
        );
        // 赚够了，不能再赚了
        if (getCount != null && getCount >= shareDailyLimit) {
            return;
        }

        ChangePointsData changePointsData = new ChangePointsData();
        changePointsData.points = shareActivePoints;
        changePointsData.pointsType = PointsTypeEnum.SHARE.getValue();
        changePointsData.relationId = shareActivePointsDto.getActiveId();
        changePointsData.title = shareActivePointsDto.getActiveTitle();
        changePointsAndAddLog(accountPo, changePointsData);
    }

    private TbUserPointsDetailPo changePointsAndAddLog(TbUserPointsAccountPo accountPo, ChangePointsData changePointsData) {
        boolean res = baseMapper.businessChangePoints(accountPo.getId(), changePointsData.points, accountPo.getRemainPoint());
        if (!res) {
            Throw.isBusinessException("积分变更失败，请重试");
        }

        TbUserPointsDetailPo detailPo = new TbUserPointsDetailPo();
        detailPo.setBusinessId(accountPo.getBusinessId());
        detailPo.setUserId(accountPo.getUserId());
        if (changePointsData.points.compareTo(BigDecimal.ZERO) > 0) {
            detailPo.setIncomeType(UserPointsIncomeTypeEnum.INCOME.getValue());
        } else {
            detailPo.setIncomeType(UserPointsIncomeTypeEnum.EXPEND.getValue());
        }
        LocalDateTime now = LocalDateTime.now();
        detailPo.setPointsType(changePointsData.pointsType);
        detailPo.setTitle(changePointsData.title);
        detailPo.setBeforePoints(accountPo.getRemainPoint());
        detailPo.setPoints(changePointsData.points);
        detailPo.setAfterPoints(changePointsData.points.add(accountPo.getRemainPoint()));
        detailPo.setDaily(Integer.valueOf(now.format(DateTimeFormatter.ofPattern("yyyyMMdd"))));
        detailPo.setRelationId(changePointsData.relationId);
        detailPo.setCouponAmount(changePointsData.couponAmount);
        detailPo.setProductPic(changePointsData.productPic);
        detailPo.setOrderId(changePointsData.orderId);
        detailPo.setCreateTime(now);
        detailPo.setUpdateTime(now);
        int insertResult = tbUserPointsDetailMapper.insert(detailPo);
        if (insertResult == 0) {
            Throw.isBusinessException("积分明细写入失败，请重试");
        }

        // 更新商家积分统计: 积分类型：1策划人调整积分 2消费积分 3转发积分 4订阅积分 5分销积分 6积分兑换 7积分订单退款
        Integer pointsType = changePointsData.pointsType;
        if (Objects.equals(pointsType, PointsTypeEnum.EXCHANGE.getValue()) || Objects.equals(pointsType, PointsTypeEnum.EXCHANGE_REFUND.getValue())) {
            tbBusinessPointsMallStatMapper.updateTotalExchangedPoints(detailPo.getBusinessId(), changePointsData.points.multiply(BigDecimal.valueOf(-1)));
        } else {
            tbBusinessPointsMallStatMapper.updateTotalPayPoints(detailPo.getBusinessId(), changePointsData.points);
        }

        return detailPo;
    }

    @Override
    public Boolean businessChangePoints(BusinessChangePointsDto businessChangePointsDto) {
        TbUserPointsAccountPo accountPo = baseMapper.getAccount(businessChangePointsDto.getBusinessId(), businessChangePointsDto.getUserId());
        if (accountPo == null) {
            Throw.isBusinessException("积分账户不存在，无法手动变更积分");
        }

        BigDecimal points = businessChangePointsDto.getPoints();
        if (points == null || points.compareTo(BigDecimal.ZERO) == 0) {
            Throw.isBusinessException("请输入变更的积分");
        }

        BigDecimal sum = points.add(accountPo.getRemainPoint());
        if (sum.compareTo(BigDecimal.ZERO) < 0) {
            Throw.isBusinessException("积分总额不能小于0");
        }
        int max = 999999999;
        if (sum.compareTo(BigDecimal.valueOf(max)) > 0) {
            Throw.isBusinessException("积分总额不能超过" + max);
        }

        TransactionStatus transaction = transactionManager.getTransaction(new DefaultTransactionAttribute());
        try {
            ChangePointsData changePointsData = new ChangePointsData();
            changePointsData.points = points;
            changePointsData.pointsType = PointsTypeEnum.BUSINESS.getValue();
            changePointsData.title = "策划人调整积分";
            changePointsAndAddLog(accountPo, changePointsData);

            transactionManager.commit(transaction);
        } catch (Exception e) {
            // 回滚事务
            transactionManager.rollback(transaction);
            log.error("<策划人手动调整积分>事务异常回滚", e);
            Throw.isBusinessException(e.getMessage());
        }

        return true;
    }

    /**
     * 积分兑换优惠券、核销商品
     */
    @Override
    public Boolean exchange(Long productId, Long userId) {
        TbBusinessPointsMallProductPo productPo = productMapper.selectById(productId);
        if (productPo == null || productPo.getDelState() != 0) {
            Throw.isBusinessException("积分商品不存在");
        }

        Integer remainNum = productPo.getRemainNum();
        if (remainNum == null || remainNum < 1) {
            Throw.isBusinessException("该商品已兑完");
        }

        TbUserPointsAccountPo accountPo = baseMapper.getAccount(productPo.getBusinessId(), userId);
        if (accountPo == null || accountPo.getRemainPoint() == null) {
            Throw.isBusinessException("积分余额不足。");
        }

        if (accountPo.getRemainPoint().compareTo(productPo.getRequiredPoints()) < 0) {
            Throw.isBusinessException("积分余额不足");
        }

        TransactionStatus transaction = transactionManager.getTransaction(new DefaultTransactionAttribute());
        try {
            boolean updateUsedNumState = productMapper.incUsedNum(productId, productPo.getRemainNum());
            if (!updateUsedNumState) {
                Throw.isBusinessException("积分商品库存更新失败，请重试");
            }

            ChangePointsData changePointsData = new ChangePointsData();
            changePointsData.points = productPo.getRequiredPoints().multiply(BigDecimal.valueOf(-1));
            changePointsData.pointsType = PointsTypeEnum.EXCHANGE.getValue();
            changePointsData.title = productPo.getGoodsName();
            changePointsData.relationId = productId;
            changePointsData.couponAmount = productPo.getAmount();
            List<String> images = productPo.getGoodsImages();
            if (images != null && !images.isEmpty()) {
                changePointsData.productPic = images.get(0);
            }
            TbUserPointsDetailPo detailPo = changePointsAndAddLog(accountPo, changePointsData);

            if (productPo.getProductType().equals(BusinessPointsMallProductTypeEnum.COUPON.getValue())) {
                // 发放积分兑换红包
                sendPointsRedPacketBill(productPo, userId);
            } else {
                // 生成积分兑换订单
                CreateOrderDto createOrderDto = new CreateOrderDto();
                createOrderDto.setUserId(userId);
                createOrderDto.setPointsProductId(productId);
                CreateOrderVo orderVo = orderApi.createPointsOrder(createOrderDto).unpack();
                if (orderVo == null || orderVo.getOrderId() == null) {
                    Throw.isBusinessException("积分订单生成失败，请重试");
                }

                detailPo.setOrderId(orderVo.getOrderId());
                tbUserPointsDetailMapper.updateById(detailPo);
            }

            transactionManager.commit(transaction);
        } catch (Exception e) {
            // 回滚事务
            transactionManager.rollback(transaction);
            log.error("<积分兑换>事务异常回滚", e);
            Throw.isBusinessException(e.getMessage());
        }

        return true;
    }

    // 订单结算，给用户、经纪人发放积分
    @Override
    public Boolean subOrderSettlePoints(OrderItemPointsSettleDto dto) {
        TransactionStatus transaction = transactionManager.getTransaction(new DefaultTransactionAttribute());
        try {
            Long businessId = dto.getBusinessId();
            Long relationId = dto.getId();
            String title = dto.getSubOrderNo() == null ? dto.getOrderNo() : dto.getSubOrderNo();

            // 给下单用户发
            sendOrderPoints(businessId, dto.getUserId(), dto.getUserPointsAmt(), relationId, title, PointsTypeEnum.CONSUME.getValue());

            // 给一级经纪人发
            sendOrderPoints(businessId, dto.getAgentId(), dto.getAgentPointsAmt(), relationId, title, PointsTypeEnum.DISTRIBUTION.getValue());

            // 给二级经纪人发
            sendOrderPoints(businessId, dto.getAgent2Id(), dto.getSubAgentPointsAmt(), relationId, title, PointsTypeEnum.DISTRIBUTION.getValue());

            transactionManager.commit(transaction);
        } catch (Exception e) {
            // 回滚事务
            transactionManager.rollback(transaction);
            log.error("<订单结算，给用户、经纪人发放积分>事务异常回滚", e);
            Throw.isBusinessException(e.getMessage());
        }

        return true;
    }

    // 积分订单取消，退回积分
    @Override
    public Boolean cancelOrderSendBackPoints(Long orderId) {
        // 已经退过，直接返回成功
        TbUserPointsDetailPo refundRecord = tbUserPointsDetailMapper.getByOrderId(orderId, PointsTypeEnum.EXCHANGE_REFUND.getValue());
        if (refundRecord != null) {
            return true;
        }

        TbUserPointsDetailPo orderRecord = tbUserPointsDetailMapper.getByOrderId(orderId, PointsTypeEnum.EXCHANGE.getValue());
        if (orderRecord == null) {
            return false;
        }

        Long productId = orderRecord.getRelationId();
        TbBusinessPointsMallProductPo productPo = productMapper.selectById(productId);
        if (productPo == null) {
            log.error("积分订单取消，退回积分, productId: {}, orderId: {}", productId, orderId);
            return false;
        }

        TbUserPointsAccountPo accountPo = baseMapper.getAccount(productPo.getBusinessId(), orderRecord.getUserId());
        if (accountPo == null) {
            log.error("积分订单取消，退回积分，积分账户不存在, productId: {}, orderId: {}, userId: {}", productId, orderId, orderRecord.getUserId());
            return false;
        }

        TransactionStatus transaction = transactionManager.getTransaction(new DefaultTransactionAttribute());
        try {
            boolean updateUsedNumState = productMapper.decUsedNum(productId, productPo.getRemainNum());
            if (!updateUsedNumState) {
                Throw.isBusinessException("积分商品库存更新失败，请重试");
            }

            ChangePointsData changePointsData = new ChangePointsData();
            changePointsData.points = orderRecord.getPoints().multiply(BigDecimal.valueOf(-1));
            changePointsData.pointsType = PointsTypeEnum.EXCHANGE_REFUND.getValue();
            changePointsData.title = orderRecord.getTitle();
            changePointsData.relationId = orderRecord.getRelationId();
            changePointsData.couponAmount = orderRecord.getCouponAmount();
            changePointsData.orderId = orderId;
            changePointsData.productPic = orderRecord.getProductPic();
            changePointsAndAddLog(accountPo, changePointsData);

            transactionManager.commit(transaction);
        } catch (Exception e) {
            // 回滚事务
            transactionManager.rollback(transaction);
            log.error("<积分订单退款>事务异常回滚", e);
            return false;
        }

        return true;
    }

    private void sendOrderPoints(Long businessId, Long userId, BigDecimal points, Long relationId, String title, Integer pointsType) {
        if (businessId == null || businessId == 0 || userId == null || userId == 0) {
            return;
        }
        if (points == null || points.compareTo(BigDecimal.ZERO) <= 0) {
            return;
        }

        TbUserPointsAccountPo accountPo = getAccountWithCreate(businessId, userId);
        if (accountPo == null || accountPo.getId() == null) {
            Throw.isBusinessException("积分账户创建失败:[" + userId + "]");
        }

        ChangePointsData changePointsData = new ChangePointsData();
        changePointsData.points = points;
        changePointsData.pointsType = pointsType;
        changePointsData.title = title;
        changePointsData.relationId = relationId;
        changePointsAndAddLog(accountPo, changePointsData);
    }

    private void sendPointsRedPacketBill(TbBusinessPointsMallProductPo productPo, Long userId) {
        PointsRedPacketDto pointsRedPacketDto = new PointsRedPacketDto();
        pointsRedPacketDto.setBusinessId(productPo.getBusinessId());
        pointsRedPacketDto.setUserId(userId);
        pointsRedPacketDto.setAmt(productPo.getAmount());
        pointsRedPacketDto.setValidDay(productPo.getDayLimit());
        pointsRedPacketDto.setFullAmount(productPo.getFullAmount());
        pointsRedPacketDto.setTitle(productPo.getGoodsName());
        Boolean state = redPacketBillApi.sendPointsRedPacketBill(pointsRedPacketDto).unpack();
        if (!state) {
            Throw.isBusinessException("优惠券生成失败，请重试");
        }
    }

    @Override
    public Page<UserPointsAccountPageVo> pageUserPointsAccount(UserPointsAccountPageDto userPointsAccountPageDto) {
        PageHelper.startPage(userPointsAccountPageDto.getPageNum(), userPointsAccountPageDto.getPageSize());
        List<UserPointsAccountPageVo> userPointsAccountPageVos = baseMapper.listUserPointsAccountByDto(userPointsAccountPageDto);
        PageInfo<UserPointsAccountPageVo> pageInfo = new PageInfo<>(userPointsAccountPageVos);
        return Page.newPageWithAllFields(pageInfo.getList(), pageInfo.getPageNum(), pageInfo.getPageSize(), pageInfo.getTotal(), pageInfo.getPages());
    }

    @Override
    public UserPointsAccountPageVo userPointsAccount(Long businessId, Long userId) {
        return baseMapper.getUserPointsAccount(businessId, userId);
    }

    @Data
    private static class ChangePointsData {
        BigDecimal points = BigDecimal.ZERO;
        Integer pointsType = 0;
        String title = "";
        Long relationId = 0L;
        BigDecimal couponAmount = BigDecimal.ZERO;
        String productPic = "";
        Long orderId = 0L;
    }
}
